/*!
OC_webtest v0.0.1
Copyright 2014 OrChange Inc. All rights reserved.
Licensed under the GPL License.
*/

/**=====================
 * Abstract Step 
*=====================*/
// Normal step, run in Step by Step method.
function Step (	object, /* Css selector */
				action, /* behiave of object */
				param
){
	
	// The data type of these are String.			
	this.object = object;
	this.action = action;
	this.param = param;
	
	// Record the time when it run by timestamp.
	this.run_timestamp = 0;
	
	// aviluable values : "ready", "doing", "fail", "ok".
	this.status = "ready";
}
Step.prototype.constructor = Step;
Step.prototype.run = function(){
	// Execute the content by string in a Step.
	// You can customs set a name in config.IFRAME_HANDLE.
	
	// Run the current step!
	this.start_time = ( new Date() ).getTime();
	
	// RUN!!!!
	(function( that ){
	
		// If enable in config, it will be highlighted here.
		if( config.SHOW_AREA_MODE === true ){
			that.highlight();
		}
		
		// DOM functions, click(), other events.
		if( that.action === "click" ){
			// Here "this" is window, but this make the script
			// could not used in "use strick" mode.
			this[config.IFRAME_HANDLE].
				contents().find( that.object ).get(0).click();
		}
		else{
			// Here "this" make the script could not used in
			// "use strick" mode.
			this[config.IFRAME_HANDLE].
				contents().find( that.object )[ that.action ]( that.param );
		}
			
	}( this ));
		
	// Refresh the status of  current step.
	//TODO full operations.
	
	return this;
}
	
// Highlight the DOM of operation. 
Step.prototype.highlight = function(){
	
	(function(that){
		_object = this[config.IFRAME_HANDLE].
					contents().find( that.object );
		
		//TODO here is not correct way
		if( _object.length === 0 ) return true;
		
		// Giva a tag by time to "id" of highlight div.
		_now = (new Date()).getTime();
		
		$('body').append( 
		  '<div style="position:absolute;index:10;background:rgba(255,255,66,0.7);top:'+
		  _object.offset().top+'px;left:'+_object.offset().left+'px;width:'+
		  _object.get(0).offsetWidth+'px;height:'+
		  _object.get(0).offsetHeight+'px;" id="'+_now+'"></div>'
		);
		
		setTimeout(function(){
			$("#"+_now).remove();
		}, config.HIGHLIGHT_REMOVE_DELAY );
		
		return false;
	})(this);
	
	return this;
}

Step.prototype.getObject = function(){
	return this.object;
}
Step.prototype.getAction = function(){
	return this.action;
}
Step.prototype.resetStatus = function(){}
Step.prototype.checkMe = function(){}
	
/**=====================
 * Step with Delay
*=====================*/
function DelayStep(
	object, /* Css selector */ 
	action, /* behiave of object */
	param,
	delay /* time of Delay | optional */
){

	// Extend Object.
	Step.call( this, object, action, param );
	
	// If no param input, make time be config.DEFAULT_DELAY .
	this.delay = delay || config.DEFAULT_DELAY;
	
	//Timer ID
	this.timer_id = 0;
	
}
DelayStep.prototype = new Step();
DelayStep.prototype.constructor = DelayStep;

/**=====================
 * Step with Event
*=====================*/
function EventStep(
	object, /* Css selector */ 
	action, /* behiave of object */
	param,
	condition, /* run in whitch condition */
	overtime /* how long to interupt */
){

	// Extend Object.
	Step.call( this, object, action );
	
	//TODO 
	this.condition = condition || "$iframe.get(0).contentWindow._dheight == 199";
	
	this.timer_id = 0;
	this.overtime || config.EVENT_TIMEOUT;
}
EventStep.prototype = new Step();
EventStep.prototype.constructor = EventStep;

/**=====================
 * Step by Step
*=====================*/

function StepbyStep(
	object, /* Css selector */ 
	action /* behiave of object */
){
	// Extend Object.
	Step.call( this, object, action );

}
EventStep.prototype = new Step();
EventStep.prototype.constructor = EventStep;

/**=====================
 * Process DEFINE!
*=====================*/
function Process(){
	
	// Save steps.
	this.queue = new Array();
	
	// A buff of Step in runtime.
	this.cur_step;
	
	// Programe counter.
	this.counter = 0;
	
	// Current loop times.
	this.loop = 0;
	
	// Record result from each step.
	this.result = {}; 
	
	// Link dictionary.
	this.dict;
};

// Core engine function.
Process.prototype = {
	
	_core : function(){

		this.cur_step.run();
		
		// Continue to execute if not complete.
		if( ++this.counter < this.getLength() ){
			(function(that){
				that.execute();
			})(this);
		}
		
		// Reset counter for next loop. But if all
		// loops have completed, return function.
		//TODO compute real loop count.
		else if ( ++this.loop < config.DEFAULT_LOOP_COUNT ){
			// I think that the use of closure function here can
			// release the _core() method of the last period.
			(function(that){
				that.counter = 0;
				that.execute();
			})(this);
		}
		else{
			//TODO something after the whole process completed.
		}
	},
	
	// Execute engine.
	execute : function(){
		that = this;
		
		// Buff current step object here.
		this.cur_step = this.queue[this.counter];
		
		// Function of Time model is here. Here "this" is the 
		// window Object. So replace by "that".
		if ( this.cur_step instanceof StepbyStep || 
			config.FOCUS_STEPBY ){
			//TODO need a control panel.
			$("#StepTrigger")
				.css("background","red").click(function(){
				$(this)
					.css( "background", "green" )
					.unbind("click");
				that._core();
			});
		}
		
		else if ( this.cur_step instanceof DelayStep ){
		
			this.cur_step.timer_id = setTimeout(
				function(){
					that._core();
				},
				
				// Control the real delay by DELAY_RATIO.
				that.cur_step.delay * 
					( config.DELAY_RATIO_MODE ? 
						config.DELAY_RATIO : 1.0 )
			);
			
		}
		
		else if ( this.cur_step instanceof EventStep ){
		
			var timeout_id = setTimeout(
				function(){
					clearInterval( that.cur_step.timer_id );
					// Here it can check time out. but may need
					// more to do.
					console.log("time out!");
				},
				config.EVENT_TIMEOUT
			);
			
			this.cur_step.timer_id = setInterval(
				function(){
					if( eval(that.cur_step.method.condition) ){
						clearInterval( that.cur_step.timer_id );
						clearTimeout( timeout_id );
						that._core();
					}
				},
				config.EVENT_TEST_CYCLE
			);
			
		}
	},
	
	// Pause a runtime process.
	pause : function(){

		// Clear timers.
		if( this.cur_step instanceof DelayStep ){
			clearTimeout( this.cur_step.method.timer_id );
		}
		else if ( this.cur_step instanceof EventStep ){
			clearInterval( this.cur_step.timer_id );
		}
		
		return this;
	},
	
	// Add one step object to Process.queue 
	addStep : function( step ){
		try{
			// Param step should be a instance of Step.
			if( !(step instanceof Step) ){ throw ERROR_BAD_STEP; }
			
			// Add new step to queue.
			this.queue.push( step );
			
			return;
		}
		catch(e){
		}
		
		return this;
	},

	restartMe : function(){

	},

	// Return the number of steps in the process.
	getLength : function(){
		return this.queue.length;
	},

	// Delete a Step from queue.
	deleteStepByIndex : function( index ){
		// Delete a step from queue. Start from 
		// index last 1.
		return this.queue.splice( index, 1 );
	},

	// Insert a Step from queue.
	insertStepByIndex : function( index, step ){
		// Insert a step at index.
		return this.queue.splice( index, 0, step );
	},

	// Set the position of queue.
	setCounter : function( pos ){
		return this.counter = pos;
	},

	// Get the position of queue.
	getCounter : function(){
		return this.counter;
	},

	// Import a queue in string array.
	importQueueFromString : function( str_queue ){
		//TODO
		_length = str_queue.length;
		for ( i=0; i<_length; i++ ){
			tmp = str_queue[i].split("|");
			this.queue.push( new Step( tmp[0], tmp[1]-0 ) );
		}
		
		return this;
	},

	// Export the queue in string array.
	exportQueueToString : function(){
		//TODO
		str_queue = new Array();
		for ( i=0; i<this.getLength(); i++ ){
			str_queue.push( this.queue[i].operator+'|'+
				this.queue[i].delay );
		}
		return str_queue;
	},

	// Drop the whole step queue.
	destroyQueue : function(){
		return this.queue = new Array();
	},

	// Output all details by string to console.
	getDetails : function(){
		//TODO
		return this;
	},

	// Confirm this Process right.
	// May use this function before execute.
	checkMe : function( step ){
		//TODO
		return this;
	},

	resetMe : function(){
	
	},

}

// Mark test.js have been loaded successfully.
script.TEST = true;